/*
** Copyright (c) 2008 Mobile GCalendarManager
**
** Permission is hereby granted, free of charge, to any person
** obtaining a copy of this software and associated documentation
** files (the "Software"), to deal in the Software without
** restriction, including without limitation the rights to use,
** copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the
** Software is furnished to do so, subject to the following
** conditions:
**
** The above copyright notice and this permission notice shall be
** included in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
** OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
** HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
** OTHER DEALINGS IN THE SOFTWARE.
*/

/**
 *
 */
package au.edu.mq.comp.itec800.mgc.ui.cli.commands;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;

import au.edu.mq.comp.itec800.mgc.MGC;
import au.edu.mq.comp.itec800.mgc.lib.calendar.GEvent;
import au.edu.mq.comp.itec800.mgc.lib.calendar.GEventManager;
import au.edu.mq.comp.itec800.mgc.lib.calendar.event.DateRange;
import au.edu.mq.comp.itec800.mgc.ui.cli.ConsoleIO;
import au.edu.mq.comp.itec800.mgc.ui.cli.Context;
import au.edu.mq.comp.itec800.mgc.ui.cli.ICommand;
import au.edu.mq.comp.itec800.mgc.util.CommandUtils;
import au.edu.mq.comp.itec800.mgc.util.GDataUtils;
import au.edu.mq.comp.itec800.mgc.util.MGCUtils;
import au.edu.mq.comp.itec800.mgc.util.StringUtils;
import au.edu.mq.comp.itec800.mgc.util.Utils;

import com.google.gdata.data.extensions.When;
import com.google.gdata.util.ServiceException;

/**
 * Calendar-context management class.
 *
 * @author Laurent Malvert [laurent.malvert@students.mq.edu.au]
 * @author Kenfe-Mickael Laventure [kenfe-mickael.laventure@students.mq.edu.au]
 */
public enum CalendarCommand implements ICommand
{
  add
  {
    @Override
    public boolean  execute(final String ... args)
    {
      boolean       succeeded = false;

      try
      {
        final GEvent  evt = MGCUtils.getGAccount().getCalendarManager().getEventManager()
          .create(MGCUtils.query(" >> Title: "       , 0, args),
                  MGCUtils.query(" >> Description: " , 1, args),
                  MGCUtils.query(" >> Location: "    , 2, args),
                  MGCUtils.query(" >> Start Time: "  , 3, args),
                  MGCUtils.query(" >> End Time: "    , 4, args));

        MGCUtils.getConsole().getIO().output(" > [%8s] %s\n",
                                             Utils.state(evt != null),
                                             evt.getShortId());
      }
      catch (IOException e)
      {
        logger.error(e.getMessage(), e);
      }
      catch (ServiceException e)
      {
        logger.error(e.getMessage(), e);
      }
      return (succeeded);
    }
  },
  edit
  {
    @Override
    public boolean        execute(final String ... args)
    {
      boolean             succeeded = false;

      if (args.length >= 1)
      {
        final GEventManager gem =
           MGCUtils.getGAccount().getCalendarManager().getEventManager();

        try
        {
          final int           idx   = Integer.parseInt(args[0]);
          final List<GEvent>  evts  = gem.getAll();

          if (Utils.isInBound(evts, idx))
          {
            String        str;
            final GEvent  evt = evts.get(idx);

            if ((args.length == 1) || (args[1].compareToIgnoreCase("Title") == 0))
            {
              evt.updateName(MGCUtils.query(" >> Title: ", 2, args));
            }
            if ((args.length == 1) || (args[1].compareToIgnoreCase("Description") == 0))
            {
              evt.updateDescription(MGCUtils.query(" >> Description: ", 2, args));
            }
            if ((args.length == 1) || (args[1].compareToIgnoreCase("Location") == 0))
            {
              evt.updateLocation(MGCUtils.query(" >> Location: ", 2, args));
            }
            if ((args.length == 1) || (args[1].compareToIgnoreCase("Time") == 0))
            {
              String  str2;

              str   = MGCUtils.query(" >> From: " , 2, args);
              str2  = MGCUtils.query(" >> To: "   , 3, args);
              if ((str.length() != 0) || (str2.length() != 0))
              {
                if (evt.getEvent().getTimes().get(0) != null)
                {
                  if (str.length() == 0)
                  {
                    str = evt.getStartTimeStamp();
                  }
                  if (str2.length() == 0)
                  {
                    str2 = evt.getEndTimeStamp();
                  }
                }
                evt.updateTime(str, str2);
              }
            }
            evt.getEvent().update();
            gem.update();
            succeeded = true;
            MGCUtils.getConsole().getIO().output(" > [%8s] %s\n",
                                                 Utils.state(succeeded),
                                                 evt.getShortId());
          }
        }
        catch (final NumberFormatException e)
        {
          logger.error(e.getMessage(), e);
        }
        catch (final IOException e)
        {
          logger.error(e.getMessage(), e);
        }
        catch (final ServiceException e)
        {
          logger.error(e.getMessage(), e);
        }
        catch (final IllegalStateException e)
        {
          logger.error(e.getMessage(), e);
        }
      }
      return (succeeded);
    }
  },
  help
  {
    @Override
    public boolean  execute(final String ... args)
    {
      return (false);
    }
  },
  list
  {
    @Override
    public boolean              execute(final String ... args)
    {
      final GEventManager       gem =
        MGCUtils.getGAccount().getCalendarManager().getEventManager();
      final String              start   = (args.length > 0 ? args[0] : DateRange.toStamp(new Date()));
      final String              end     = (args.length > 1 ? args[1] : null);

      try
      {
        final List<GEvent>      evts  = gem.getAll();
        int                     idx   = 0;

        for (final GEvent e : evts)
        {
          //TODO: command line parsing need to be updated to allow "2008.10.10 10:10:10"
          if (e.isInRange(start, end))
          {
            MGCUtils.getConsole().getIO().output(" %2d. [%s - %s] %s\n",
                                                 idx,
                                                 e.getStartTimeStamp().replace('T', ' '),
                                                 e.getEndTimeStamp().replace('T', ' '),
                                                 e.getName());
          }
          idx++;
        }
      }
      catch (final IOException e)
      {
        logger.error(e.getMessage(), e);
      }
      catch (final ServiceException e)
      {
        logger.error(e.getMessage(), e);
      }
      return (false);
    }
  },
  noop
  {
    @Override
    public boolean  execute(final String ... args)
    {
      return (true);  //  does nothing but does it well
    }
  },
  quickadd
  {
    @Override
    public boolean              execute(final String ... args)
    {
      final GEventManager       gem =
        MGCUtils.getGAccount().getCalendarManager().getEventManager();
      final ConsoleIO           io  = MGCUtils.getConsole().getIO();

      for (final String arg : args)
      {
        try
        {
          final GEvent  e = gem.quickAdd(arg);

          io.output(" > [%8s] %s\n", Utils.state(e != null), e.getShortId());
          // TODO: quote parsing needs to be finished.
        }
        catch (final IOException e)
        {
          logger.error(e.getMessage(), e);
        }
        catch (final ServiceException e)
        {
          logger.error(e.getMessage(), e);
        }
      }
      return (true);
    }
  },
  update
  {
    @Override
    public boolean  execute(final String ... args)
    {
      try
      {
        MGCUtils.getGAccount().getCalendarManager().getEventManager().update();
        MGC.getInstance().setContext(Context.calendar);
      }
      catch (IOException e)
      {
        logger.error(e.getMessage(), e);
      }
      catch (ServiceException e)
      {
        logger.error(e.getMessage(), e);
      }
      return (true);
    }
  },
  remove
  {
    @Override
    public boolean              execute(final String ... args)
    {
      final GEventManager       gem       =
        MGCUtils.getGAccount().getCalendarManager().getEventManager();
      final StringBuffer        out       = new StringBuffer(512);
      boolean                   succeeded = false;

      try
      {
        final List<GEvent>  evts      = gem.getAll();
        final List<GEvent>  toRemove  = new ArrayList<GEvent>();

        for (final String arg : args)
        {
          try
          {
            final int id  = Integer.parseInt(StringUtils.clean(arg));

            if (Utils.isInBound(evts, id))
            {
              final GEvent  evt = evts.get(id);

              if (succeeded = GEventManager.delete(evt))
              {
                toRemove.add(evt);
              }
              out.append(String.format(" > [%8s] %2s - %s\n",
                                       Utils.state(succeeded),
                                       id,
                                       evt.getName()));
            }
          }
          catch (NumberFormatException e)
          {

          }
        }
        succeeded = true;
        evts.removeAll(toRemove);
        MGCUtils.getConsole().getIO().output(out.toString());
      }
      catch (final IOException e)
      {
        logger.error(e.getMessage(), e);
      }
      catch (final ServiceException e)
      {
        logger.error(e.getMessage(), e);
      }
      return (succeeded);
    }
  },
  view
  {
    @Override
    public boolean  execute(final String ... args)
    {
      final GEventManager gem =
        MGCUtils.getGAccount().getCalendarManager().getEventManager();
      boolean       succeeded = false;

      try
      {
        final List<GEvent>  evts  = gem.getAll();
        int                 idx   = (args.length > 0) ? Integer.parseInt(args[0]) : 0;

        if (Utils.isInBound(evts, idx))
        {
          final GEvent        evt       = evts.get(idx);
          final StringBuffer  view      = new StringBuffer(512);
          final List<When>    evtTimes  = evt.getEvent().getTimes();

          view.append(" >> Title: ");
          view.append(evt.getName());
          view.append("\n >> Location: ");
          view.append(evt.getLocation());
          if (evtTimes.size() > 0)
          {
            final When  wh  = evtTimes.get(0);

            view.append("\n >> Times: ");
            view.append("\n   >> From: ");
            view.append(String.format("%1$ta %1$td %1$tb %1$tY %1$tH:%1$tM",
                                      GDataUtils.fromDateTime(wh.getStartTime())));
            view.append("\n   >> To  : ");
            view.append(String.format("%1$ta %1$td %1$tb %1$tY %1$tH:%1$tM",
                                      GDataUtils.fromDateTime(wh.getEndTime())));
          }
          view.append("\n >> Description: ");
          view.append(evt.getDescription());
          view.append("\n\n");
          MGCUtils.getConsole().getIO().output(view.toString());
        }
      }
      catch (final NumberFormatException e)
      {
        logger.error(e.getMessage(), e);
      }
      catch (final IOException e)
      {
        logger.error(e.getMessage(), e);
      }
      catch (final ServiceException e)
      {
        logger.error(e.getMessage(), e);
      }
      return (succeeded);
    }
  },

  /*
   * short forms / aliases
   */
  a (add),
  e (edit),
  h (help),
  l (list),
  q (quickadd),
  r (remove),
  v (view);


  private static final Logger logger    = Logger.getLogger(CalendarCommand.class);

  private String              hlp       = "";
  private ICommand            aliases[] = null;
  private ICommand            master    = null;


  private CalendarCommand()
  {
    this(null, null);
  }

  private CalendarCommand(final String hlp)
  {
    this(hlp, null);
  }

  private CalendarCommand(final ICommand master)
  {
    this(null, master);
  }

  private CalendarCommand(final String hlp, final ICommand master)
  {
    this.master = ((master != null) && (master.name().equals(name()) == false)) ? master : null;
    this.hlp    = (hlp != null) ? hlp : ((this.master != null) ? this.master.help() : "");
  }


  public boolean          execute(final String ... args)
  {
    return (isAlias() ? getMaster().execute(args) : false);
  }

  public boolean          needsAuthentication()
  {
    return (false);
  }

  public ICommand         getMaster()
  {
    return (master);
  }

  public boolean          isAlias()
  {
    return (CommandUtils.isAlias(this));
  }

  public ICommand         getTopMaster()
  {
    return (CommandUtils.getTopMaster(this));
  }

  public String           help()
  {
    return ("");
  }

}
